#pragma once

#include "World Includes.h"
#include "World Definitions.h"
#include "Item/Items.h"
#include "../Global References/VectorContainer.h"
#include "Entity/Entity.h"
#include "Entity/Animation.h"

//As long as this enums values are in the same order as they are added in LoadAssets
// AllAssets.GetbyIndex(<insertType>_ASSET_INDEX) can be used to reference the block quickly
enum BlockAssetIndex{
	/*AIR_ASSET_INDEX,*/
	DIRT_ASSET_INDEX,
	GRASS_ASSET_INDEX,
	ROCK_ASSET_INDEX,
	TREE_ASSET_INDEX,
	LEAF_ASSET_INDEX,
	ORE1_ASSET_INDEX,
	LIMIT_ASSET_INDEX,
	WIN_ASSET_INDEX,
	NULL_ASSET_INDEX,
	ASSET_INDEX_SIZE
};

static char* worldBlockNames[ASSET_INDEX_SIZE] = {
	/*"YOU ARE MAGIC",*/
	"Foam",
	"Turf",
	"Scrap",
	"Fiber",
	"Algae Wire",
	"Inorite",
	"Limiter",
	"Trophy",
	"NULL"
};

//Conversions
/*
World position = block position

World postion to chunk array position
int i = (x/y) / CHUNK_SIZE_(H/W)

World position to chunk position
int i = (x/y) / CHUNK_SIZE_(H/W)
i * CHUNK_SIZE_(H/W)

World position to block array position
int i = (x/y) / CHUNK_SIZE_(H/W)
i * CHUNK_SIZE(H/W)
int j = (x/y)
j = j - i
j /= BLOCK_TEXTURE_SIZE
*/

class _World {
	//Public is first so that its easier to find
public:
	static _World * Ins() {
		static _World W;
		return & W;
	}

	// BNNC BY :
	// FinishAllChanges();



	//Reference world definitions for return values
	// Search (ctrl+f) WORLD_RETURN_VALUES

///////////////////INITIALIZER FUNCTIONS///////////////////
//** All the functions that can initialize the game    **//
	//Both of these call InitializeNewExB
	int const InitializeNew( bool Overwrite );
	int const InitializeNewExA( int ChunkAmountH, int ChunkAmountW, bool Overwrite );

	//a_SaveDir is the directory in which all the world data for this specific save is
	//a_MainSaveDir is the directory in which all the save files are
	//Setups up a new directory for a new save location
	int const InitializeNewExB( char * a_MainSaveDir, char * a_SaveDir, int ChunkAmountH, int ChunkAmountW, bool Overwrite );
	
	//Trys to load the files from an already existing directory,
	// If the directory is invalid, it creates a new one with that name
	int const InitializeExisting( char * a_MainSaveDir, char * a_SaveDir, int ChunkAmountH, int ChunkAmountW );


///////////////////SETUP FUNCTIONS///////////////////
//** All the functions that setup the game data  **//

	//Sets the players position
	// Loads all the information into the current list of chunks based off of players position
	int const SetupGameplay();

	int const AddDrawList();

	int const InitPlayer();

	int const InitEntities(int Density,int TotalCount);

	int const InitServerEntities();

	//Loads and initializes all of the entities
	int const SetupEntities(int Density);

	//Resets all the entities positions
	int const ResetEntities();

	//Creates basic information for the texts files that the world references
	// This information sets the upper half of the world to air and the lower
	// half of the world to dirt
	int const SetupNewTextFiles(std::ofstream & out,int ChunkAmountH, int ChunkAmountW);
	int const ResetTextFiles(std::ofstream & out,int ChunkAmountH, int ChunkAmountW);

	//Calls
	// - SetupNewTextFiles
	// - SetupBiomes
	// * Sets the world seed if its not already set
	// - Generator.Init and Generator.Generate
	int const SetupNewSaveFile();

	int const ResetSaveFile();

	int const SetupExistingSaveFile();

	//Setup biomes is not complete yet
	//Loads all of the biomes in the directory if there are any. If not it loads them from the example
	// directory. If there are none in the example directory it fails.
	int const SetupNewBiomes(std::ifstream & in);

	int const SetupExistingBiomes(std::ifstream & in);

	//Creates the Main directory if it hasn't already been created.
	// If its allowed to overwrite, it saves the current char* as its FullPath.
	// If its not, it creates a char* with "MainDir//a_SaveDir#//". The number is incremented until
	// it doesn't find a match
	int const SetupDirectory( char * MainDir , char * a_SaveDir , bool a_Overwrite );

	//Attempts to use the current char* as the FullPath if it valid. If it is invalid, it calls setup directory
	bool const VerifyDirectory( char * MainDir, char * a_SaveDir );

///////////////////Asset Functions///////////////////

	//Loads all the types of blocks textures into memory
	int const LoadAssets();
	
	//Sets any assets IDs for blocks that have not already been set
	int const SetAssets(_Chunk * C);

	//Finds the matching icon asset for a block
	int GetAssetArtID(Block_Info * ToMatch) 
	{
		for(int i = AllAssets.size() - 1;i >= 0;i--) {
			if(AllAssets.getByIndex(i).Type == ToMatch->Data->GetType_s())
				return i;
		}
		return 0;
	}

	const Vector2D GetIconAsset(Block_Info * ToMatch) {
		for(int i = AllAssets.size() - 1;i >= 0;i--) {
			if(AllAssets.getByIndex(i).Type == ToMatch->Data->GetType_s())
				return AllIcons.getByIndex(i);
		}
	}

///////////////////Networking Accessors///////////////////
	void SetSaveData(char** data, int a_numFiles) {
		if(rawMultiSaveData)
		{
			for(int i = 0; i < numFiles; ++i)
				delete rawMultiSaveData[i];
			delete rawMultiSaveData;
		}
		numFiles = a_numFiles;
		rawMultiSaveData = data; 
	}
	char** GetSaveData() { return rawMultiSaveData; }

	Entity* getEntity(unsigned i);
	Entity* getEntityByID(unsigned i);
	int getNumEntities();
	int getNumSaveFiles() { return MapW * MapH; }
	int getMapW() { return MapW; }
	int getMapH() { return MapH; }

	bool VerifyPath() {
		if( MainSavePath && MySavePath ) {
			if(FullPath) delete FullPath;
			FullPath = new char[128];
			sprintf_s(FullPath, 128, "%s//%s//",MainSavePath,MySavePath);
			return true;
		}
		return false;
	}
	char* GetPathDir() { if(VerifyPath()) return FullPath; else return "NULL";}
	char* GetPath2MainSaveDir() { return MainSavePath; }
	char* GetPath2SaveDir() { return MySavePath; }
	void SetSeed(unsigned int a_Seed){WorldSeed = a_Seed;}

	void AddChange(WorldChange * NewChange) {
		AllChanges.push(NewChange);
	}


///////////////////GAMEPLAY CHUNK FUNCTIONS///////////////////
//** Any function that is needed at game play time  **//
//** to keep the world chunks up to date			**//
	//Checks if the chunk the player is currently in has changed
	// If it has, it returns the new chunk
	_Chunk * DetermineChunkLoc(float X,float Y,float Z);
	//Uses DetermineChunkLoc to check if it needs to run
	// If so, it uses the chunk passed back to reload all of the
	// surrounding chunks
	int const ReloadChunks(float X,float Y,float Z);
	//Does the same thing as ReloadChunks except it reloads all
	// nearby chunks whether the player has moved from one to 
	// another or not
	int const ForceReload(float X,float Y,float Z,bool Save);
	//Determines if an entity is close enough to the player to keep
	// updating or, if it is invalid memory space
	bool DetermineEntityChunkLoc(float X,float Y,float Z);
	//This function will attempt to complete as many changes as it is allowed before exiting
	// If any changes are within the currently loaded scope of memory, they always take priority
	// otherwise a single chunk is loaded, the changes are completed and the function exits
	int const ProcessChanges();
	//Clears out the change list so nothing is left over in a new world
	int const ClearChanges();

///////////////////GAMEPLAY UPDATING///////////////////
	
	int addNewPlayer(Entity* e);	//Adds a new player to the entity list		
	void addNewEntity(Entity* e);	//Adds a new enemy to the entity list
	void removeEntity(short index);	//Removes an entity from the entity list
	void removePlayerEntities();	//Removes all entities besides the first player

	void Update();					//Player(s), Enemy, Projectile. In that order
	int const PlayerUpdate();		//Calls the player's update, acquires a new drawlist & collision list
	int const EnemyUpdate();		//Updates enemies within range of character.
	int const ProjectileUpdate();	//Move projectiles and check for collision
	void playSoundsforPlayer(bool* activesounds);

	/*		COLLISION CHECKS	*/
	int countToTen;
	void Collide(Entity * E, bool isPlayer);
	RetVal const	dataBlockCollide(pointQuad a_Q);		//Check a quad and get collision data back
	bool const		boolPointCollide(Vector2D toCheck);		//Check a 2d for a true/false of collision
	bool const		boolBlockCollide(pointQuad toCheck);	//Check a quad for a true/false of collision
	void ProjectileCollide(Entity * E, pointQuad ERect);	//Checks an entity against all the nearby projectiles

	/*		BLOCK PLACEMENT		*/
	bool placeBlock(Item_Projectile* collider, RetVal Data);
	int	 determineBlockTraitByName(char* Name);
	int	 determineBlockGraphicByName(char* Name);
	
	/*		FILL LISTS			*/
	void GetBlockList(Vector2D a_Pos, float Dist, Block_Info ** List, int * ListSize,int MaxSize);	
	//Fils bottom to top, right to left
	void GetSpiralBlockList	(Vector2D a_Pos,float Dist, Block_Info ** List,int * ListSize,int MaxSize);		
	//Fills from outside in
	void GetDrawList(float const & x,float const & y);	
	//Fills using: GetSpiralBlockList, player position  
	void GetCollisionList(float const & x,float const & y,int const & a_Dist);	
	//Fills the collision list - Uses GetBlockList
	int const GetProjectileCollisionList(Entity* E, int * ListSize,int const MaxSize,int const & a_Dist);  
	//Fills the projectile collision list - Uses GetBlockList
	bool CheckListBoundaries(int & bx,int & cx,int & by,int & cy); 
	//Checks an x and y coodinate and makes sure they are within the range of 0-49

	int const Regenerate();

	int const ResetPlayer(Entity * E,bool Dead);


///////////////////GAMEPLAY SAVING & LOADING///////////////////

	//Saves all the chunks that are currently loaded into memory
	// Also creates a save.txt file. This file includes extra information that
	// will be important to loading an existing save file
	int const Save();

	//Saves all the surrounding chunks
	int const SaveCurrent();

	//Not yet built - Added by Michael?
	void CreateSaveFile(char* data);

	//
	char** ConcatenateSaveInfo();

///////////////////GAMEPLAY CLEANUP///////////////////

	//Deletes all the text files in a save directory
	int DeleteWorld(char* WorldDirectory);

	//Cleans up all the memory. Calls the save function and closes down the world.
	int const CleanUp();

	int const CleanLists();

///////////////////PLAYERS MOVEMENT///////////////////
	int FindGroundLevel(int const & x,bool Save) {
		return GENERATOR::Ins()->FindOpenGroundHeight(x,Save);
	}

///////////////////PLAYERS & DEBUG ENTITIES///////////////////
	bool PlayerReset;
	Entity * Players[4];
	//Entity * TestEnemy;
	//Entity * TestEnemy2;

///////////////////ANIMATION///////////////////
Animation animationEngine;
///////////////////PRIVATE VARIABLES///////////////////
private:
	
	_World() : Map(NULL),
		FullPath(NULL),
		MainSavePath(NULL),
		MySavePath(NULL),
		DrawListSize(0),
		MapH(0),MapW(0),
		WorldSeed(0),
		rawMultiSaveData(NULL),
		numFiles(0),
		PlayerReset(false)
	{
		for(int i = 9-1;i >= 0;i--) {
			CurrentLoc[i] = NULL;
			BufferSpace[i] = NULL;
		}
		DrawList = new Block_Info*[MAX_LIST_SIZE];
		CollideList = new Block_Info*[MAX_LIST_SIZE];
		ProjectileCollideList = new Item_Projectile*[MAX_PROJECTILES];
		AllEntities = new Vector<Entity*>;
	};

	// TODO BY
	// This needs to be done when AI FullPath is added to the chunk
	// It will be called on each chunk inside of updateallchunks
	// void UpdateAIPath();	

	//All of the chunks that can be loaded
	_Chunk * Map;

	//The FullPath to the saved text files
	char * FullPath;
	char * MainSavePath,* MySavePath;

	//The amount of chunks, the Height and Width
	short MapH,MapW;

	//For Generating the world safely
	unsigned int WorldSeed;
	char** rawMultiSaveData;
	int numFiles;

	//All the objects that are able to draw and in view distance
	int DrawListSize;
	Block_Info ** DrawList;// Size = MAX_DRAW_LIST

	int CollideListSize;
	Block_Info ** CollideList;// Size = MAX_COLLIDE_LIST

	int ProjectileCollideListSize;
	Item_Projectile ** ProjectileCollideList;// Size = MAX_PROJECTILES

	//The 9 chunks that can be loaded at a time
	// This memory will be handed to the current chunks that
	// are loaded so they can store their data
	_Chunk * CurrentLoc[9];
	Block * BufferSpace[9];

	//This holds all the asset IDs for the blocks in game
	Vector<AssetIDLink> AllAssets;
	Vector<Vector2D> AllIcons;

	//A vector to hold all the entities in game,
	// including the player
	Vector<Entity*> * AllEntities;
	byte Density;
	byte TotalCount;

	//A Vector to hold all of the changes that have happend on a different client
	Vector<WorldChange*> AllChanges;
}typedef WORLD_MODULE,* PWORLD_MODULE;